package org.cy.evolution.ioc.compiler;

import com.google.auto.service.AutoService;

import org.cy.evolution.ioc.annotation.AutoInject;
import org.cy.evolution.ioc.compiler.generator.AutoInjectGenerator;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

@AutoService(Processor.class)
public class AutoInjectProcessor extends AbstractProcessor {

    // 文件工具类
    private Filer filer;

    // 元素工具类
    private Elements elements;

    // 日志消息工具类
    private Messager messager;

    private Map<String, AutoInjectGenerator> injectGenerators = new HashMap<>(0);

    /**
     * 每一个注解处理器类都必须有一个空的构造函数。
     * 然而，这里有一个特殊的init()方法，它会被注解处理工具调用，
     * 并输入ProcessingEnv参数。
     *
     * @param processingEnv 处理环境
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        filer = processingEnv.getFiler();
        elements = processingEnv.getElementUtils();
        messager = processingEnv.getMessager();
    }

    /**
     * 这相当于每个处理器的主函数main()，你在这里写你的扫描、评估和处理注解的代码，以及生成Java文件。
     * 输入参数RoundEnvironment，可以让你查询出包含特定注解的被注解元素
     *
     * @param annotations 请求处理的注解类型
     * @param roundEnv 有关当前和以前的信息环境
     * @return 如果返回 true，则这些注解已声明并且不要求后续 Processor 处理它们；
     * 如果返回 false，则这些注解未声明并且可能要求后续 Processor 处理它们
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        injectGenerators.clear();
        for (TypeElement typeElem : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElem)) {
                TypeElement classElement = (TypeElement) element.getEnclosingElement();
                String fullName = classElement.getQualifiedName().toString();
                AutoInjectGenerator proxyInjector = injectGenerators.get(fullName);
                if (null == proxyInjector) {
                    proxyInjector = new AutoInjectGenerator(elements, classElement, this.messager);
                    injectGenerators.put(fullName, proxyInjector);
                }
                proxyInjector.add(element);
            }
        }
        // 循环生成文件
        for (AutoInjectGenerator autoInjectGenerator : injectGenerators.values()) {
            try {
                autoInjectGenerator.generate().writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 这里必须指定，这个注解处理器是注册给哪个注解的。注意，它的返回值是一个字符串的集合，包含本处理器想要处理的注解类型的合法全称
     *
     * @return 注解器所支持的注解类型集合，如果没有这样的类型，则返回一个空集合
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>(0);
        types.add(AutoInject.class.getCanonicalName());
        return types;
    }

    /**
     * 指定使用的Java版本，通常这里返回SourceVersion.latestSupported()，默认返回SourceVersion.RELEASE_6
     *
     * @return 使用的Java版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
